﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TicketMaster.Object;
using Wintellect.PowerCollections;
using TicketMaster.Data;


namespace TicketMaster.Services
{
    public class ReservationPathSelector
    {
        public List<Route> getPath(City from, City to)
        {
            List<Route> result = new List<Route>();
            var data = new UowData();
            IRepository<City> cities = data.Cities;

            var citiesObj = cities.All().ToList();
            int count = cities.All().Count();
            Route[] parent = new Route[count + 1];    //holds the previous node in the found path, described as Route object in order to preserve the route data
            double[] distances = new double[count + 1];     //holds the length of best current path from "from"
            bool[] visited = new bool[count + 1];     //keeps track of the visited nodes
            for (int i = 1; i <= count; i++)
            {
                distances[i] = Double.MaxValue;
                visited[i] = false;
            }

            Pair<double, City> currentNode = new Pair<double, City>(0.0, from);
            distances[from.Id] = 0.0;

            OrderedBag<Pair<double, City>> queue = new OrderedBag<Pair<double, City>>(); //the elements are sorted in ascending order
            queue.Add(currentNode);

            while (queue.Count() != 0)
            {
                currentNode = queue.GetFirst();  //queue.top()
                queue.RemoveFirst();             //queue.pop()
                City node = currentNode.Second;
                if (visited[node.Id]) continue;
                visited[node.Id] = true;

                List<Route> childrenRoutes = getChildren(node, data);
                foreach (Route childRoute in childrenRoutes) 
                {
                    City next = childRoute.EndPoint;
                    if (!visited[next.Id] && distances[next.Id] > distances[node.Id] + childRoute.Distance) 
                    {
                        distances[next.Id] = distances[node.Id] + childRoute.Distance;
                        parent[next.Id] = childRoute;
                        queue.Add(new Pair<double, City>(distances[next.Id], next));
                    }
                }
            }

            City city = to;
            while (city.Id != from.Id)
            {
                result.Add(parent[city.Id]);
                city = parent[city.Id].StartPoint;
            }


            result.Reverse();
            return result;
        }

        //returns a list of the children of the given city
        public List<Route> getChildren(City city, IUowData data)
        {
            IRepository<Route> routes = data.Routes;
            var routesObj = routes.All().ToList();
            List<Route> children = new List<Route>();
            int routesCount = routesObj.Count;

            for (int i = 0; i < routesCount; i++)
            {
                Route route = routesObj[i];

                if (route != null)
                {
                    if (route.StartPoint.Id != city.Id) continue;
                    children.Add(route);    
                }
                
            }

            return children;
        }
    }
}
